package Servlet;

import Entity.Compass;
import Entity.GameLevel;
import Entity.Transportation;
import Entity.Waypoint;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.naming.Context;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author erico
 */
@PersistenceContext(name = "persistence/LogicalName", unitName = "GooWherePU")
public class MainController extends HttpServlet {

    @Resource
    private javax.transaction.UserTransaction utx;

    /** 
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */


    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            /* TODO output your page here*/
            if (request.getParameter("dir") != null) {
                String dirtext = request.getParameter("dir");
                String[] dir = dirtext.split(",");
                //dir[0] is the direction in String
                //dir[1] is latitude
                //dir[2] is longitude
                if (!dir[0].equals("") && !dir[1].equals("") && !dir[2].equals("")) {
                    //Go to DB and find any transportations available in such dir
                    List<Transportation> t = findByDirection(dir[0], Double.parseDouble(dir[1]), Double.parseDouble(dir[2]));
                    if (t == null) {
                        out.print("No transportation available from such location.");
                    } else {
                        StringBuffer output = new StringBuffer();
                        for (Transportation trans : t) {
                            if (Double.parseDouble(dir[1]) == trans.getWaypoint3().getLatitude() && Double.parseDouble(dir[2]) == trans.getWaypoint3().getLongitude()) {
                                output.append(trans.getTransportationType().getTransportType() + "," + trans.getWaypoint().getLatitude() + "&" + trans.getWaypoint().getLongitude() + "," + trans.getTransportationType().getCostPerKm() + "," + trans.getTransportationType().getSpeedKms() + "," + trans.getWaypoint().getName());
                            } else {
                                output.append(trans.getTransportationType().getTransportType() + "," + trans.getWaypoint3().getLatitude() + "&" + trans.getWaypoint3().getLongitude() + "," + trans.getTransportationType().getCostPerKm() + "," + trans.getTransportationType().getSpeedKms() + "," + trans.getWaypoint3().getName());
                            }
                            output.append(";");
                        }
                        output.replace(output.lastIndexOf(";"), output.length(), "");
                        out.print(output.toString());
                    }
                } else {
                    //Print error screen
                    out.print("Error direction input");
                }
            } else if (request.getParameter("level") != null) {
                //calls when user advanced to next level,
                //returns all needed info for the next lev
                String level = request.getParameter("level");
                GameLevel levInfo = getLevelInfo(Integer.parseInt(level));
                out.print(levInfo.getWaypoint1().getLatitude() + "," + levInfo.getWaypoint1().getLongitude() + "," + levInfo.getWaypoint2().getLatitude() + "," + levInfo.getWaypoint2().getLongitude() + "," + levInfo.getStartMoney() + "," + levInfo.getWaypoint1().getName());
            } else if (request.getParameter("user") != null) {
                //a user has logged in, return map according to user's last level
            } else {
                //This happens when the page initializes, setting all the attributes in index.jsp
                GameLevel levInfo;
                if (request.getAttribute("levelNum") != null) {
                    levInfo = getLevelInfo((Integer) request.getAttribute("levelNum"));
                } else {
                    levInfo = getLevelInfo(1);
                }

                //sets the Session attributes for index.jsp to access them
                request.setAttribute("waypoint1Lat", levInfo.getWaypoint1().getLatitude());
                request.setAttribute("waypoint1Lng", levInfo.getWaypoint1().getLongitude());
                request.setAttribute("waypoint2Lat", levInfo.getWaypoint2().getLatitude());
                request.setAttribute("waypoint2Lng", levInfo.getWaypoint2().getLongitude());
                request.setAttribute("startMoney", levInfo.getStartMoney());
                request.setAttribute("curLocation", levInfo.getWaypoint1().getName());
                request.setAttribute("currentUser", "");

                request.getRequestDispatcher("index.jsp").forward(request, response);
            }
        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", e);
            throw new RuntimeException(e);
        } finally {
            out.close();
        }
    }

//Gets each level's information such as the start & end waypoints, start money
    public GameLevel getLevelInfo(int level) {
        GameLevel info = null;
        try {
            Context ctx = GetContext.getContext();
            utx.begin();
            EntityManager em = (EntityManager) ctx.lookup("persistence/LogicalName");
            info = (GameLevel) em.createNamedQuery("GameLevel.findByLevelNo").setParameter("levelNo", level).getSingleResult();
            utx.commit();
        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", e);
            throw new RuntimeException(e);
        }

        return info;
    }

//Look for the different transportations available from a given lat & lng from DB
    public List<Transportation> findByDirection(String dir, double curLat, double curLng) {
        List<Transportation> t = null;
        Waypoint w = null;
        Compass c = null;
        Query q = null;
        try {
            Context ctx = GetContext.getContext();
            utx.begin();
            EntityManager em = (EntityManager) ctx.lookup("persistence/LogicalName");
            q = em.createNamedQuery("Compass.findByDescription").setParameter("description", dir);
            List compass = q.getResultList();
            if (!compass.isEmpty()) {
                c = (Compass) compass.get(0); //find the compass representing a dirction
                System.out.println("Compass: " + ((Compass)compass.get(0)).getDirection());
                q = em.createNamedQuery("Waypoint.findByClosestLatLng").setParameter("lat", curLat).setParameter("long", curLng); //find the waypoint of current location
                List waypoints = q.getResultList();
                if (!waypoints.isEmpty()) {
                    w = (Waypoint) waypoints.get(0);
                    System.out.println("Waypoint: "+((Waypoint) waypoints.get(0)).getName());
                    //get the transportations available at that waypoint
                    q = em.createNamedQuery("Transportation.findByDirection").setParameter("point", w).setParameter("dir", c);
                    t = q.getResultList();
                    System.out.println("Transportation: " + t.toString());
                    if (!t.isEmpty()) {
                        utx.commit();
                    } else {
                        t = null;
                    }

                } else {
                    t = null;
                }

            } else {
                t = null;
            }

        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", e);
            throw new RuntimeException(e);
        }

        return t;
    }

// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>

    protected void persist(Object object) {
        try {
            Context ctx = GetContext.getContext();
            utx.begin();
            EntityManager em = (EntityManager) ctx.lookup("persistence/LogicalName");
            em.persist(object);
            utx.commit();
        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", e);
            throw new RuntimeException(e);
        }
    }
}
